﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
using System.Security.Cryptography;
using System.IO;

namespace OpenDynDnsChanger.Cryption
{
    public class CryptionHelper
    {
        const string REGNAME_SECURITY = "Software\\OpenDynDnsChanger\\Security";
        const string REGNAME_CONFIG = "Software\\OpenDynDnsChanger\\Config";
        const string REGNAME_PW = "Password";
        const string REGNAME_IV = "PW_IV";
        const string REGNAME_KEY = "PW_Key";

        System.Security.Cryptography.SymmetricAlgorithm alg;
        

        public CryptionHelper()
        {
            alg = System.Security.Cryptography.SymmetricAlgorithm.Create();

            if (!KeysExists())
            {
                alg.GenerateIV();
                alg.GenerateKey();
                SetKey(alg);
            }
            else
            {
                RegistryKey secKey = Registry.LocalMachine.OpenSubKey(REGNAME_SECURITY);

                alg.IV = (byte[])secKey.GetValue(REGNAME_IV);
                alg.Key = (byte[])secKey.GetValue(REGNAME_KEY);

                secKey.Close();
            }
        }


        /// <summary>
        /// Gets or sets the uncrypted password for the Api-Call Credentials
        /// </summary>
        public string Password
        {
            get
            {
                try
                {
                    RegistryKey configKey = Registry.LocalMachine.OpenSubKey(REGNAME_CONFIG);

                    ICryptoTransform transform = alg.CreateDecryptor();

                    byte[] cryptedPassword = (byte[])configKey.GetValue(REGNAME_PW);

                    MemoryStream csBase = new MemoryStream();
                    csBase.Write(cryptedPassword, 0, cryptedPassword.Length);

                    csBase.Seek(0, SeekOrigin.Begin);

                    CryptoStream crpStream = new CryptoStream(csBase, transform, CryptoStreamMode.Read);

                    StreamReader uncrpReader = new StreamReader(crpStream, System.Text.UnicodeEncoding.Unicode);

                    string uncrpPassword = uncrpReader.ReadToEnd();

                    csBase.Close();
                    csBase.Dispose();

                    return uncrpPassword;

                }
                catch
                {
                    return "";
                }
            }
            set
            {
                try
                {
                    RegistryKey configKey = Registry.LocalMachine.OpenSubKey(REGNAME_CONFIG, true);

                    ICryptoTransform transform = alg.CreateEncryptor();

                    MemoryStream csBase = new MemoryStream();
                    CryptoStream crpStream = new CryptoStream(csBase, transform, CryptoStreamMode.Write);

                    byte[] clearValue = System.Text.UnicodeEncoding.Unicode.GetBytes(value);

                    crpStream.Write(clearValue, 0, clearValue.Length);
                    crpStream.FlushFinalBlock();

                    csBase.Seek(0, SeekOrigin.Begin);

                    byte[] crpBuffer = new byte[csBase.Length];
                    csBase.Read(crpBuffer, 0, (int)csBase.Length);

                    configKey.SetValue(REGNAME_PW, crpBuffer);

                    csBase.Close();
                    csBase.Dispose();
                }
                catch
                {
                    
                }
            }
        }

        /// <summary>
        /// Save the Key from a SymmetricAlgorithm
        /// </summary>
        private void SetKey(System.Security.Cryptography.SymmetricAlgorithm alg)
        {
            RegistryKey secKey = Registry.LocalMachine.OpenSubKey(REGNAME_SECURITY, RegistryKeyPermissionCheck.ReadWriteSubTree, System.Security.AccessControl.RegistryRights.SetValue);

            try
            {
                secKey.SetValue(REGNAME_IV, alg.IV);
                secKey.SetValue(REGNAME_KEY, alg.Key);
            }
            catch (Exception ex)
            {

            }

        }

        /// <summary>
        /// True if IV and SymmetricAlgorithm Key exist in Registry.
        /// </summary>
        /// <returns></returns>
        private bool KeysExists()
        {

            RegistryKey secKey = Registry.LocalMachine.OpenSubKey(REGNAME_SECURITY);

            if (secKey == null)
                return false;

            bool value = secKey.GetValue(REGNAME_IV, null) != null &&
                secKey.GetValue(REGNAME_KEY, null) != null;

            secKey.Close();

            return value;

        }

    }
}
